/*
 * Copyright (c) 2013-2017 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * ----------------------------------------------------------------------
 *
 * $Date:        1. December 2017
 * $Revision:    V2.0.0
 *
 * Project:      CMSIS-DAP Source
 * Title:        SW_DP.c CMSIS-DAP SW DP I/O
 *
 *---------------------------------------------------------------------------*/

#include "DAP_config.h"
#include "DAP.h"




#define H_SWD_Transfer_Fast_CLK_High oIP_GPIO_SetOutput_High(PIN_SWCLK_TCK_PORT,PIN_SWCLK_TCK_BIT) ;__NOP();__NOP()//;__NOP();__NOP()
#define H_SWD_Transfer_Fast_CLK_Low  oIP_GPIO_SetOutput_Low(PIN_SWCLK_TCK_PORT,PIN_SWCLK_TCK_BIT)  ;__NOP();__NOP()//;__NOP();__NOP()


#define H_SWD_Transfer_CLK_High      oIP_GPIO_SetOutput_High(PIN_SWCLK_TCK_PORT,PIN_SWCLK_TCK_BIT) ;PIN_DELAY_SLOW(DAP_Data.clock_delay)
#define H_SWD_Transfer_CLK_Low       oIP_GPIO_SetOutput_Low(PIN_SWCLK_TCK_PORT,PIN_SWCLK_TCK_BIT)  ;PIN_DELAY_SLOW(DAP_Data.clock_delay)


#define H_SWD_Transfer_DIO_High      oIP_GPIO_SetOutput_High(PIN_SWDIO_TMS_PORT,PIN_SWDIO_TMS_BIT)
#define H_SWD_Transfer_DIO_Low       oIP_GPIO_SetOutput_Low(PIN_SWDIO_TMS_PORT,PIN_SWDIO_TMS_BIT)

#define H_SWD_Transfer_DIO_Bit       oIP_GPIO_GetInputBit(PIN_SWDIO_TMS_PORT,PIN_SWDIO_TMS_BIT)



// Generate SWJ Sequence
//	count:  sequence bit count
//	data:	pointer to sequence bit data
//	return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))

void SWJ_Sequence(uint32_t count,const uint8_t *data)
{
	uint8_t Value;
	uint8_t Msk;

	while(count>7)
	{
		count-=8;

		Value=*data;
		data=&data[1];

		Msk=0x01U;
		while(Msk!=0U){
			
			if (Value & Msk)
			{
				H_SWD_Transfer_DIO_High;
			}
			else
			{
				H_SWD_Transfer_DIO_Low;
			}
			H_SWD_Transfer_CLK_Low;
			Msk<<=1;
			H_SWD_Transfer_CLK_High;
		}

	}
	Value=data[0];
	while(count>0)
	{
		
		if (Value & 0x01U)
		{
			H_SWD_Transfer_DIO_High;
		}
		else
		{
			H_SWD_Transfer_DIO_Low;
		}
		H_SWD_Transfer_CLK_Low;
		count--;
		Value >>= 1;
		H_SWD_Transfer_CLK_High;

	}
}
#endif


// Generate SWD Sequence
//   info:   sequence information
//   swdo:   pointer to SWDIO generated data
//   swdi:   pointer to SWDIO captured data
//   return: none
#if (DAP_SWD != 0)
void SWD_Sequence(uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
  uint32_t val;
  uint32_t bit;
  uint32_t n, k;

  n = info & SWD_SEQUENCE_CLK;
  if (n == 0U) {
    n = 64U;
  }

  if (info & SWD_SEQUENCE_DIN) {
    while (n) {
      val = 0U;
      for (k = 8U; k && n; k--, n--) {

        H_SWD_Transfer_CLK_Low;
				if(H_SWD_Transfer_DIO_Bit)
				{
					bit=0x01U;
				}
				else{
					bit=0x00U;
				}
				H_SWD_Transfer_CLK_High;


        val >>= 1;
        val  |= bit << 7;
      }
      val >>= k;
      *swdi++ = (uint8_t)val;
    }
  } else {
    while (n) {
      val = *swdo++;
      for (k = 8U; k && n; k--, n--) {

        if (val & 0x01U)
				{
					H_SWD_Transfer_DIO_High;
				}
				else
				{
					H_SWD_Transfer_DIO_Low;
				}
				H_SWD_Transfer_CLK_Low;
				val >>= 1;
				H_SWD_Transfer_CLK_High;
        
      }
    }
  }
}
#endif


#if (DAP_SWD != 0)


unsigned char H_SWD_Fast_Transfer(unsigned int request, unsigned int* data){

	unsigned int Parity;
	unsigned int Value;
	unsigned int Msk;
	unsigned int n;
	unsigned char Ack;


	PIN_SWDIO_OUT_ENABLE();

	Parity=0U;

	//Start Bit
	H_SWD_Transfer_DIO_High;
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//APnDP Bit
	if (request & 0x01U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//RnW Bit
	if (request & 0x02U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//A2 Bit
	if (request & 0x04U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//A3 Bit
	if (request & 0x08U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//Parity Bit
	if (Parity & 0x01U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//Stop Bit
	H_SWD_Transfer_DIO_Low;
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;

	//Park Bit
	H_SWD_Transfer_DIO_High;
	H_SWD_Transfer_Fast_CLK_Low;
	H_SWD_Transfer_Fast_CLK_High;



	//Turnaround
	PIN_SWDIO_OUT_DISABLE();
	n=DAP_Data.swd_conf.turnaround;
	while(n)
	{
		H_SWD_Transfer_Fast_CLK_Low;
		n--;
		H_SWD_Transfer_Fast_CLK_High;
	}

	//Acknowledge response
	H_SWD_Transfer_Fast_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack=0x01U;
	}
	else{
		Ack=0x00U;
	}
	H_SWD_Transfer_Fast_CLK_High;
	H_SWD_Transfer_Fast_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack|=0x02U;
	}
	H_SWD_Transfer_Fast_CLK_High;
	H_SWD_Transfer_Fast_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack|=0x04U;
	}
	H_SWD_Transfer_Fast_CLK_High;



	if(Ack == DAP_TRANSFER_OK)
	{
		//OK response
		//Data transfer
		if(request & DAP_TRANSFER_RnW)
		{
			//Read data
			Value=0U;
			Parity=0U;
			Msk=0x00000001U;

			//Read RDATA[0:31]
			while(Msk!=0U){
				H_SWD_Transfer_Fast_CLK_Low;
				if(H_SWD_Transfer_DIO_Bit)
				{
					Value|=Msk;
					Parity+=1U;
				}
				H_SWD_Transfer_Fast_CLK_High;
				Msk<<=1;
			}

			//Read Parity
			H_SWD_Transfer_Fast_CLK_Low;
			if(H_SWD_Transfer_DIO_Bit)
			{
				if(Parity&0x1U)
				{
				}
				else{
					Ack = DAP_TRANSFER_ERROR;
				}
			}
			else{
				if(Parity&0x1U)
				{
					Ack = DAP_TRANSFER_ERROR;
				}
				else{
				}
			}
			H_SWD_Transfer_Fast_CLK_High;


			if(data){
				*data = Value;
			}

			//Turnaround
			n=DAP_Data.swd_conf.turnaround;
			while(n)
			{
				H_SWD_Transfer_Fast_CLK_Low;
				n--;
				H_SWD_Transfer_Fast_CLK_High;
			}

			PIN_SWDIO_OUT_ENABLE();
		}
		else
		{
			//Turnaround
			n=DAP_Data.swd_conf.turnaround;
			while(n)
			{
				H_SWD_Transfer_Fast_CLK_Low;
				n--;
				H_SWD_Transfer_Fast_CLK_High;
			}

			PIN_SWDIO_OUT_ENABLE();

			//Write data
			Value=data[0];
			Parity=0U;
			Msk=0x00000001U;

			//Write WDATA[0:31]
			while(Msk!=0U){
				if(Value & Msk)
				{
					H_SWD_Transfer_DIO_High;
					Parity+=1U;
				}
				else{
					H_SWD_Transfer_DIO_Low;
				}
				H_SWD_Transfer_Fast_CLK_Low;
				Msk<<=1;
				H_SWD_Transfer_Fast_CLK_High;
			}

			//Write Parity Bit
			if(Parity & 0x1U)
			{
				H_SWD_Transfer_DIO_High;
			}
			else{
				H_SWD_Transfer_DIO_Low;
			}
			H_SWD_Transfer_Fast_CLK_Low;
			H_SWD_Transfer_Fast_CLK_High;
		}

		//Idle cycles
		n = DAP_Data.transfer.idle_cycles;
		if(n!=0)
		{
			H_SWD_Transfer_DIO_Low;
			while(n)
			{
				H_SWD_Transfer_Fast_CLK_Low;
				n--;
				H_SWD_Transfer_Fast_CLK_High;
			}
		}
		H_SWD_Transfer_DIO_High;

		return Ack;
	}


	if((Ack==DAP_TRANSFER_WAIT)||(Ack==DAP_TRANSFER_FAULT))
	{
		//WAIT or FAULT response
		if(DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0))
		{
			//Dummy Read RDATA[0:31] + Parity
			n=32+1;
			while(n)
			{
				H_SWD_Transfer_Fast_CLK_Low;
				n--;
				H_SWD_Transfer_Fast_CLK_High;
			}
		}
		//Turnaround
		n = DAP_Data.swd_conf.turnaround;
		while(n)
		{
			H_SWD_Transfer_Fast_CLK_Low;
			n--;
			H_SWD_Transfer_Fast_CLK_High;
		}

		PIN_SWDIO_OUT_ENABLE();
		if(DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0))
		{
			H_SWD_Transfer_DIO_Low;
			//Dummy Write WDATA[0:31] + Parity
			n=32+1;
			while(n)
			{
				H_SWD_Transfer_Fast_CLK_Low;
				n--;
				H_SWD_Transfer_Fast_CLK_High;
			}
		}
		H_SWD_Transfer_DIO_High;

		return Ack;
	}

	n = DAP_Data.swd_conf.turnaround + 32 + 1;
	while(n)
	{
		H_SWD_Transfer_Fast_CLK_Low;
		n--;
		H_SWD_Transfer_Fast_CLK_High;
	}
	H_SWD_Transfer_DIO_High;

	return Ack;
}

unsigned char H_SWD_Transfer(unsigned int request, unsigned int* data){

	unsigned int Parity;
	unsigned int Value;
	unsigned int Msk;
	unsigned int n;
	unsigned char Ack;


	PIN_SWDIO_OUT_ENABLE();

	Parity=0U;

	//Start Bit
	H_SWD_Transfer_DIO_High;
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//APnDP Bit
	if (request & 0x01U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//RnW Bit
	if (request & 0x02U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//A2 Bit
	if (request & 0x04U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//A3 Bit
	if (request & 0x08U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//Parity Bit
	if (Parity & 0x01U)
	{
		H_SWD_Transfer_DIO_High;
		Parity+=1U;
	}
	else
	{
		H_SWD_Transfer_DIO_Low;
	}
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//Stop Bit
	H_SWD_Transfer_DIO_Low;
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;

	//Park Bit
	H_SWD_Transfer_DIO_High;
	H_SWD_Transfer_CLK_Low;
	H_SWD_Transfer_CLK_High;



	//Turnaround
	PIN_SWDIO_OUT_DISABLE();
	n=DAP_Data.swd_conf.turnaround;
	while(n)
	{
		H_SWD_Transfer_CLK_Low;
		n--;
		H_SWD_Transfer_CLK_High;
	}

	//Acknowledge response
	H_SWD_Transfer_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack=0x01U;
	}
	else{
		Ack=0x00U;
	}
	H_SWD_Transfer_CLK_High;
	H_SWD_Transfer_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack|=0x02U;
	}
	H_SWD_Transfer_CLK_High;
	H_SWD_Transfer_CLK_Low;
	if(H_SWD_Transfer_DIO_Bit)
	{
		Ack|=0x04U;
	}
	H_SWD_Transfer_CLK_High;



	if(Ack == DAP_TRANSFER_OK)
	{
		//OK response
		//Data transfer
		if(request & DAP_TRANSFER_RnW)
		{
			//Read data
			Value=0U;
			Parity=0U;
			Msk=0x00000001U;

			//Read RDATA[0:31]
			while(Msk!=0U){
				H_SWD_Transfer_CLK_Low;
				if(H_SWD_Transfer_DIO_Bit)
				{
					Value|=Msk;
					Parity+=1U;
				}
				H_SWD_Transfer_CLK_High;
				Msk<<=1;
			}

			//Read Parity
			H_SWD_Transfer_CLK_Low;
			if(H_SWD_Transfer_DIO_Bit)
			{
				if(Parity&0x1U)
				{
				}
				else{
					Ack = DAP_TRANSFER_ERROR;
				}
			}
			else{
				if(Parity&0x1U)
				{
					Ack = DAP_TRANSFER_ERROR;
				}
				else{
				}
			}
			H_SWD_Transfer_CLK_High;


			if(data){
				*data = Value;
			}

			//Turnaround
			n=DAP_Data.swd_conf.turnaround;
			while(n)
			{
				H_SWD_Transfer_CLK_Low;
				n--;
				H_SWD_Transfer_CLK_High;
			}

			PIN_SWDIO_OUT_ENABLE();
		}
		else
		{
			//Turnaround
			n=DAP_Data.swd_conf.turnaround;
			while(n)
			{
				H_SWD_Transfer_CLK_Low;
				n--;
				H_SWD_Transfer_CLK_High;
			}

			PIN_SWDIO_OUT_ENABLE();

			//Write data
			Value=data[0];
			Parity=0U;
			Msk=0x00000001U;

			//Write WDATA[0:31]
			while(Msk!=0U){
				if(Value & Msk)
				{
					H_SWD_Transfer_DIO_High;
					Parity+=1U;
				}
				else{
					H_SWD_Transfer_DIO_Low;
				}
				H_SWD_Transfer_CLK_Low;
				Msk<<=1;
				H_SWD_Transfer_CLK_High;
			}

			//Write Parity Bit
			if(Parity & 0x1U)
			{
				H_SWD_Transfer_DIO_High;
			}
			else{
				H_SWD_Transfer_DIO_Low;
			}
			H_SWD_Transfer_CLK_Low;
			H_SWD_Transfer_CLK_High;
		}

		//Idle cycles
		n = DAP_Data.transfer.idle_cycles;
		if(n!=0)
		{
			H_SWD_Transfer_DIO_Low;
			while(n)
			{
				H_SWD_Transfer_CLK_Low;
				n--;
				H_SWD_Transfer_CLK_High;
			}
		}
		H_SWD_Transfer_DIO_High;

		return Ack;
	}


	if((Ack==DAP_TRANSFER_WAIT)||(Ack==DAP_TRANSFER_FAULT))
	{
		//WAIT or FAULT response
		if(DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0))
		{
			//Dummy Read RDATA[0:31] + Parity
			n=32+1;
			while(n)
			{
				H_SWD_Transfer_CLK_Low;
				n--;
				H_SWD_Transfer_CLK_High;
			}
		}
		//Turnaround
		n = DAP_Data.swd_conf.turnaround;
		while(n)
		{
			H_SWD_Transfer_CLK_Low;
			n--;
			H_SWD_Transfer_CLK_High;
		}

		PIN_SWDIO_OUT_ENABLE();
		if(DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0))
		{
			H_SWD_Transfer_DIO_Low;
			//Dummy Write WDATA[0:31] + Parity
			n=32+1;
			while(n)
			{
				H_SWD_Transfer_CLK_Low;
				n--;
				H_SWD_Transfer_CLK_High;
			}
		}
		H_SWD_Transfer_DIO_High;

		return Ack;
	}

	n = DAP_Data.swd_conf.turnaround + 32 + 1;
	while(n)
	{
		H_SWD_Transfer_CLK_Low;
		n--;
		H_SWD_Transfer_CLK_High;
	}
	H_SWD_Transfer_DIO_High;

	return Ack;
}





// SWD Transfer I/O
//	request: A[3:2] RnW APnDP
//	data:	DATA[31:0]
//	return:  ACK[2:0]
uint8_t  SWD_Transfer (uint32_t request, uint32_t *data){

	if(DAP_IsFastSpeed!=0){
		return H_SWD_Fast_Transfer(request, data);
	}
	else{
		return H_SWD_Transfer(request, data);
	}
}

void LineToSWD(){

	unsigned char Value_c;

	PIN_SWDIO_OUT_ENABLE();
	H_SWD_Transfer_CLK_High;
	H_SWD_Transfer_DIO_Low;

	Value_c=0xFFU;
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);

	Value_c=0x9EU;
	SWJ_Sequence(8,&Value_c);
	Value_c=0xE7U;
	SWJ_Sequence(8,&Value_c);

	Value_c=0xFFU;
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	SWJ_Sequence(8,&Value_c);
	Value_c=0x00U;
	SWJ_Sequence(8,&Value_c);

}

void vResetTarget(uint8_t bit)
{
	unsigned int Val;

	if((bit&1U)==0)
	{

		//LineToSWD();

		Val=0xE000ED0C;
		SWD_Transfer(0xC5U,&Val);//set AIRCR address
		Val=0x05FA0007;
		SWD_Transfer(0xDDU,&Val);//set RESET data
	}
}


#endif  /* (DAP_SWD != 0) */
